/**
 * Title :	jFamilyTree generator
 * Description: jFamilyTree generator
 * Version:	provider
 * Copyright (c) 2001-2008
 * Terms of use:see license agreement at http://treebolic.sourceforge.net/en/license.htm
 * Author: Bernard Bou
 * Company: bsys
 */
package jFamilyTree.generator.domtree;

import java.util.Vector;

import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class TreeModelAdapter implements TreeModel
{
	// D A T A

	/**
	 * DOM document
	 */
	private Document theDocument;

	/**
	 * From node
	 */
	private String theFromNode;

	/**
	 * Whether to consider DOM elements
	 */
	private boolean elementsOnly = false;

	/**
	 * Listener support
	 */
	private Vector<TreeModelListener> theListenerList = new Vector<TreeModelListener>();

	// C O N S T R U C T

	/**
	 * Constructor
	 * 
	 * @param thisDocument
	 *        document
	 * @param thisFromNode
	 *        from node
	 * @param thisElementsOnlyFlag
	 *        whether to consider elements only
	 */
	public TreeModelAdapter(Document thisDocument, String thisFromNode, boolean thisElementsOnlyFlag)
	{
		theDocument = thisDocument;
		theFromNode = thisFromNode;
		elementsOnly = thisElementsOnlyFlag;
	}

	// T R E E M O D E L

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.tree.TreeModel#getRoot()
	 */
	public Object getRoot()
	{
		if (theFromNode == null)
			return theDocument.getDocumentElement();
		else
		{
			NodeList theseElements = theDocument.getElementsByTagName(theFromNode);
			return (theseElements == null || theseElements.getLength() < 1) ? null : theseElements.item(0);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.tree.TreeModel#isLeaf(java.lang.Object)
	 */
	public boolean isLeaf(Object thisNode)
	{
		return childCountOf((Node) thisNode) == 0;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.tree.TreeModel#getChildCount(java.lang.Object)
	 */
	public int getChildCount(Object thisNode)
	{
		return childCountOf((Node) thisNode);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.tree.TreeModel#getChild(java.lang.Object, int)
	 */
	public Object getChild(Object thisNode, int thisIndex)
	{
		return nthChildOf((Node) thisNode, thisIndex);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.tree.TreeModel#getIndexOfChild(java.lang.Object, java.lang.Object)
	 */
	public int getIndexOfChild(Object thisNode, Object thisChild)
	{
		return indexOf((Node) thisNode, (Node) thisChild);
	}

	// M O D I F I C A T I O N

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.tree.TreeModel#valueForPathChanged(javax.swing.tree.TreePath, java.lang.Object)
	 */
	public void valueForPathChanged(TreePath path, Object newValue)
	{
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.tree.TreeModel#addTreeModelListener(javax.swing.event.TreeModelListener)
	 */
	public void addTreeModelListener(TreeModelListener thisListener)
	{
		if (thisListener != null && !theListenerList.contains(thisListener))
			theListenerList.addElement(thisListener);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.tree.TreeModel#removeTreeModelListener(javax.swing.event.TreeModelListener)
	 */
	public void removeTreeModelListener(TreeModelListener thisListener)
	{
		if (thisListener != null)
			theListenerList.removeElement(thisListener);
	}

	// T R E E M O D E L . H E L P E R S

	/**
	 * Get nth child node
	 * 
	 * @param thisNode
	 *        node
	 * @param thatIndex
	 *        index
	 * @return nth child
	 */
	private Node nthChildOf(Node thisNode, int thatIndex)
	{
		if (!elementsOnly)
			return thisNode.getChildNodes().item(thatIndex);

		// nth element node
		int thisIndex = 0;
		for (int i = 0; i < thisNode.getChildNodes().getLength(); i++)
		{
			Node thisChild = thisNode.getChildNodes().item(i);
			if (thisChild.getNodeType() == Node.ELEMENT_NODE && thisIndex++ == thatIndex)
				return thisChild;
		}
		return null;
	}

	/**
	 * Get child count
	 * 
	 * @param thisNode
	 *        node
	 * @return number of children
	 */
	private int childCountOf(Node thisNode)
	{
		if (!elementsOnly)
			return thisNode.getChildNodes().getLength();

		// number of element nodes
		int thisCount = 0;
		for (int i = 0; i < thisNode.getChildNodes().getLength(); i++)
		{
			Node thisChild = thisNode.getChildNodes().item(i);
			if (thisChild.getNodeType() == Node.ELEMENT_NODE)
				++thisCount;
		}
		return thisCount;
	}

	/**
	 * Get index of child node
	 * 
	 * @param thisNode
	 *        node
	 * @param thatChild
	 *        child
	 * @return index or -1 if not found
	 */
	private int indexOf(Node thisNode, Node thatChild)
	{
		int thisCount = childCountOf(thisNode);
		for (int i = 0; i < thisCount; i++)
		{
			Node thisChild = nthChildOf(thisNode, i);
			if (thatChild == thisChild)
				return i;
		}
		return -1;
	}
}
